/*
* Copyright (c) 2014 Qunar.com. All Rights Reserved.
*/
package net.oschina.bilbodai.common.beanutil.property.asm.lexer.code;

import net.oschina.bilbodai.common.beanutil.property.asm.lexer.code.list.ListTypes;
import net.oschina.bilbodai.common.beanutil.property.asm.lexer.code.mp.MapKeyTypeMapping;
import net.oschina.bilbodai.common.beanutil.property.asm.lexer.code.mp.MapKeyTypes;
import net.oschina.bilbodai.common.beanutil.property.ValueTypeMappings;
import net.oschina.bilbodai.common.beanutil.util.CollectionUtils;
import net.oschina.bilbodai.common.beanutil.util.StringUtil;
import net.oschina.bilbodai.common.beanutil.util.TypeUtil;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;

/**
 * @author qt-maodai  Date: 15-8-8 Time: 下午5:05
 * @version $Id$
 */
public class CodeAppliers {

    //token 可能是 propertyName(name), mapKey(<string>id)或者index(1)
    public static ICodeApplier getCodeApplierByToken(String token, Class<?> beanType, Class<?> stackType, List<Class<?>> genericTypes) throws Exception {

        if (token == null || beanType == null) {
            throw new NullPointerException("获取'" + token + "'属性值为null，无法继续获取余下的属性值, 如果是Map，请检查key的类型是否正确");
        }

        CodeApply.Builder builder = CodeApply.newBuilder();

        builder.withParentType(beanType);//with parent
        builder.withStackType(stackType); //stack type
        if (Map.class.isAssignableFrom(beanType)) {
            String keyType = null;
            Class<?> valueType = Object.class;
            if (!CollectionUtils.isEmpty(genericTypes) && genericTypes.size() == 2) {
                keyType = MapKeyTypeMapping.getTypeNameByType(genericTypes.get(0));
                valueType = genericTypes.get(1);
            }
            MapKeyTypes.MapKeySplitter mapKeySplitter = MapKeyTypes.splitKey(token);
            if (keyType == null) {
                keyType = mapKeySplitter.getKeyType();
                if (StringUtil.isNotBlank(mapKeySplitter.getValueType())) {
                    valueType = ValueTypeMappings.getMapValueType(mapKeySplitter.getValueType());
                }
            }

            builder.withMapKey(mapKeySplitter.getKey())
                    .withMapKeyType(keyType);

            return new MapCodeApplier(builder.build(), valueType);

        } else if (beanType.isArray()) {
            return new ArrayCodeApplier(builder.build(), Integer.parseInt(token));

        } else if (List.class.isAssignableFrom(beanType)) {
            Class<?> valueType = Object.class;
            if (!CollectionUtils.isEmpty(genericTypes) && genericTypes.size() == 1) {
                valueType = genericTypes.get(0);
            }
            String index = token;
            if (valueType == Object.class) {
                ListTypes.ListValueTypeSplitter splitter = ListTypes.splitKey(token);
                index = splitter.getKey();
                if (splitter.hasValueType()) {
                    valueType = ValueTypeMappings.getMapValueType(splitter.getValueType());
                }
            }
            return new ListCodeApplier(builder.build(), Integer.parseInt(index), valueType);

        } else {
            Field property = TypeUtil.getFieldByName(token, beanType);
            builder.withThisPropertyName(token)
                    .withThisGenericTypes(TypeUtil.getGenericTypes(property))
                    .withThisType(property.getType());
            return new POJOBeanCodeApplier(builder.build());
        }

    }

}